<?php

namespace App\Http\Controllers\Api;

use App\Enums\AssetStatus;
use App\Enums\ProductStatus;
use App\Enums\ProductType;
use App\Enums\RightsType;
use App\Exceptions\ApiException;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\AssetKey;
use App\Models\Order;
use App\Models\Product;
use App\Models\ProductConfig;
use App\Services\WalletService;
use DB;
use Illuminate\Support\Arr;

class AssetController extends Controller
{
    /** @var WalletService */
    private $walletService;

    /**
     * AssetController constructor.
     */
    public function __construct(WalletService $walletService)
    {
        $this->walletService = $walletService;
    }

    /** 查询我的资产 */
    public function listMyAssets()
    {
        $page = request()->page ?: 1;
        $limit = request()->limit ?: 10;

        $query = $this->getMyAssetQuery();

        $count = $query->count([DB::raw('distinct contract_address')]);
        $assetProducts = $query
            ->select([
                'contract_address',
                DB::raw('max(id) as max_id'),
                DB::raw('count(*) as token_count'),
            ])->groupBy('contract_address')
            ->offset(($page - 1) * $limit)->limit($limit)
            ->orderByDesc('max_id')
            ->get();

        $products = Product::query()
            ->with(['category'])
            ->whereIn('contract_address', Arr::pluck($assetProducts, 'contract_address'))
            ->where('type', ProductType::ISSUE)
            ->get()->mapWithKeys(function (Product $product) {
                return [$product->contract_address => $product];
            });

        $list = [];
        foreach ($assetProducts as $item) {
            /** @var Product $product */
            $product = Arr::get($products, $item->contract_address);
            if ($product) {
                $list[] = [
                    'contract_address' => $item->contract_address,
                    'product_id' => $product->id,
                    'category' => $product && $product->category ? $product->category->name : '',
                    'name' => $product ? $product->name : '',
                    'logo_url' => $product ? $product->logo_url : '',
                    'issuer' => $product ? $product->issuer : '',
                    'price' => $product ? $product->display_price : '',
                    'token_count' => $item->token_count,
                ];
            }
        }

        return [
            'count' => $count,
            'list' => $list,
        ];
    }

    /** 查询我的资产存证列表 */
    public function listMyAssetTokens()
    {
        request()->validate([
            'contract_address' => 'nullable',
            'order_id' => 'nullable',
        ]);
        $page = request()->page ?: 1;
        $limit = request()->limit ?: 10;

        $query = $this->getMyAssetQuery();
        if (request()->contract_address) {
            $product = Product::query()->where('contract_address', request()->contract_address)->first();

            $query->where('contract_address', request()->contract_address);
        } elseif (request()->order_id) {
            $order = Order::query()->find(request()->order_id);
            $product = $order->product;
            $query->where('order_id', request()->order_id);
        } else {
            throw new ApiException('参数中必须包含contract_address或order_id');
        }
        $resaleConfigs = $product->configs()
            ->whereIn('config_key', [
                ProductConfig::RESALE_FORBID,
                ProductConfig::RESALE_DURATION,
                ProductConfig::GIVE_FORBID,
                ProductConfig::GIVE_DURATION,
            ])
            ->get()
            ->mapWithKeys(function (ProductConfig $config) {
                return [$config->config_key => $config->config_value];
            })->toArray();
        $resaleForbid = (bool) Arr::get($resaleConfigs, ProductConfig::RESALE_FORBID, true);
        $resaleDuration = Arr::get($resaleConfigs, ProductConfig::RESALE_DURATION, 0);
        $giveForbid = (bool) Arr::get($resaleConfigs, ProductConfig::GIVE_FORBID, true);
        $giveDuration = Arr::get($resaleConfigs, ProductConfig::GIVE_DURATION, 0);
        $count = $query->count();
        $canExchangeCountQuery = $query;
        $canExchangeCount = 0;
        $list = $query
            ->offset(($page - 1) * $limit)->limit($limit)
            ->orderByDesc('id')
            ->get()->map(function (Asset $asset) use ($resaleForbid, $resaleDuration, $canExchangeCount) {
                $assetCanResale = !$resaleForbid && $asset->created_at->addDays($resaleDuration)->lt(now());

                if ($asset->asset_status->is(AssetStatus::HOLD())) {
                    ++$canExchangeCount;
                }

                return [
                    'order_asset_id' => $asset->id,
                    'token_id' => $asset->token_id,
                    'trans_hash' => $asset->trans_hash,
                    'cert_sn' => $asset->cert_sn,
                    'asset_can_resale' => $assetCanResale,
                    'on_resale' => $asset->asset_status->is(AssetStatus::ON_SALE()),
                    'asset_can_exchange' => $asset->asset_status->is(AssetStatus::HOLD()),
                    'is_exchanged' => $asset->asset_status->is(AssetStatus::EXCHANGED()),
                    'create_time' => $asset->created_at->toDateTimeString(),
                ];
            });

        if ($product->rights_type->is(RightsType::ENTITY)) {
            if ($product->entity && $product->entity->entity->status->is(ProductStatus::ON_SALE)) {
                $rightsEntity = true;
                $entityId = $product->entity->entity_id;
            }
        }

        return [
            'count' => $count,
            'product_can_resale' => !$resaleForbid,
            'resale_duration' => $resaleDuration,
            'product_can_give' => !$giveForbid,
            'give_duration' => $giveDuration,
            'rights_entity' => $rightsEntity ?? false,
            'entity_id' => $entityId ?? null,
            'can_exchange_count' => $canExchangeCountQuery->where('asset_status', AssetStatus::HOLD)->count(),
            'list' => $list,
        ];
    }

    /** 查询资产存证信息 */
    public function getAssetEvidence($assetId)
    {
        $asset = Asset::query()->find($assetId);
        if (!$asset) {
            throw new ApiException('资产未找到');
        }
        $transEvidence = $this->walletService->tokenTransEvidence($asset->token_id, $asset->trans_hash);

        return [
            'name' => Arr::get($transEvidence, 'assetsName'),
            'category' => Arr::get($transEvidence, 'assetsCategoryCN'),
            'cert_sn' => Arr::get($transEvidence, 'certSN'),
            'trans_hash' => Arr::get($transEvidence, 'hash'),
            'holder' => hide_mobile(Arr::get($transEvidence, 'holder')),
            'original_holder' => hide_mobile(Arr::get($transEvidence, 'originalHolder')),
            'create_time' => Arr::get($transEvidence, 'ptime'),
            'trans_time' => Arr::get($transEvidence, 'ttime'),
        ];
    }

    /**
     * @return mixed
     */
    public function listMyAssetKey()
    {
        return AssetKey::where('asset_id', request()->asset_id)
            ->first();
    }

    private function getMyAssetQuery()
    {
        return Asset::query()
            ->where('user_id', auth()->id())
            ->whereIn('asset_status', [AssetStatus::HOLD, AssetStatus::ON_SALE, AssetStatus::EXCHANGED]);
    }
}
